Introduction
This article continues our series on GLUT, the GL Utility Toolkit
written by Mark Kilgard for OpenGL applications. As we mentioned in
our previous article (Windows
and Animations) GLUT is a very interesting and useful toolkit
for any OpenGL developer because it lets you write portable code.
GLUT hides the developer from the dirty details of the underlying
windows manager and GUI interface.
GLUT is divided into a number of subAPIs. In this month's issue
we will describe the Windows Management subAPI. As its name
indicates, it takes care of tasks related to the windows used by
your OpenGL application: creating, destroying, iconifying a window;
pushing, popping, hiding, moving; and setting titles, positions,
etc..
Windows Management
Sub-Api
Here is a full list of the functions supported by the windows
management in GLUT (As of version 3.6):
| int glutCreateWindow(char *name) |
Creates a new top-level window |
int glutCreateSubWindow(int win, int
x, int y, int width, int height) |
Creates a sub-window |
| void glutSetWindow(int winId) |
Set the window with ID winId as current window |
| int glutGetWindow(void) |
Requests the identifier for the current window |
| void glutDestroyWindow(int winId) |
Destroys the window specified by winId |
| void glutPostRedisplay(void) |
Tells the GLUT event processor that the current window
needs to be redisplayed |
| void glutSwapBuffers(void) |
Swaps the buffers for the current window |
| void glutPositionWindow(int x, int y) |
Requests a change in the position of the window |
| void glutReshapeWindow(int width, int height) |
Requests a change in the size of the window |
| void glutFullScreen() |
Requests that the current window be made full screen
|
void glutPopWindow(void) void
glutPushWindow(void) |
Push or Pop the current window relative to others in the
stack |
void glutShowWindow(void) void
glutHideWindow(void) void glutIconifyWindow(void) |
Show, hide or iconify the current window |
void glutSetWindowTitle(char *name) void
glutSetIconTitle(char *name) |
Set title bar in window or iconified
window |
Using
Sub-Windows
The use of most of the functions above is very simple. Some were
reviewed in our first article: glutPostRedisplay,
glutCreateWindow, glutPositionWindow, glutSwapBuffers,..etc.
Others although new are trivial to use and the above descriptions
pretty much say what they do, like glutSetIconTitle,
glutFullScreen, ...etc. However the use of sub-Windows is not
that simple so we decided to give you next a simple example and
discuss the details of its implementation.
Here is the source code for a little OpenGL-GLUT demo (example1.c,
Makefile).
Its purpose is to show you: (a) How to handle a subwindow, (b) How
to use your keyboard to interact with your OpenGL application (c)
How to render text in an OpenGL window. (Please print out or
have the source code for example1.c at hand while we follow the
discussion.)
![[Snapshot of example1 running]](GLUT programming Windows Management_soubory/glut2.jpeg)
First let us take a look at the main() function. It starts as any
other GLUT application with initializations: parsing the command
line options, selecting the display mode and setting the position
and size of the initial window. Since our application is going to
handle more than one window it is necessary to store the integer
window ID returned by the glutCreateWindow statement. The
variable winIdMain is a window handle. Then it is time to set
up the callback functions for the events associated window
winIdMain; several callback functions are defined and all
perform a task on the main window: a display function
(mainDisplay) that draws the scene, a reshape function
(mainReshape) that handles any transformation of the window
frame, keyboard that handles the actions triggered by the keyboard
and idle for managing the animation when no other events are pending
(see Windows
and Animations for a more detailed description of idle's
role);
The first important thing to know is that in a GLUT application
there can only be one idle callback function. The idle
function is global for all the windows in the application. So take
this into account when designing your idle() functions,
they must take care of refreshing all windows and subwindows in your
application.
Next in the code comes the creation a subwindow
(winIDSub). To make a sub-window you must provide the ID for
the top level window, in the present case winIDMain, the x
and y coordinates in pixels for the subwindow relative to the
internal coordinates of winIDMain, and the width and height
in pixels of the requested sub-window. After creating the subwindow
GLUT returns a handle to our program and we are ready to set up the
appropriate callback functions for winIDSub. In our demo we
set two callback functions: a display (subDisplay) and a reshape
(subReshape).
When GLUT opens a subwindow it provides it with a full OpenGL
context. There is then a performance penalty on using subwindows
because the driver of the graphics card has to refresh the memory
area for each of the windows on separate passes. Thanks to having
independent OpenGL contexts, each window has its own system of
coordinates, for example. In example1.c the coordinate systems are
set in mainDisplay() and subDisplay()
respectively. Now go and examine these two display functions, they
are quite simple and if you followed our January article on OpenGL
("Simple
Polygon Rendering") you will have no trouble understanding it.
The function mainDisplay() draws a triangle with Red,
Green and Blue vertices. OpenGL interpolates colors between the
three vertices to fill the polygon. Before rendering the triangle we
have added a glRotate statement that rotates the triangle
around the z-axis (perpendicular to the window screen), the angle of
rotation (spin) is slowly increased in idle() to
give the illusion of a rotating figure.
As for the display function associated with winIdSub it is
also very straight forward. First it clears the background with a
grey color, then it draws a green border around the subwindow and
finally it renders some text. Later we will explain how text
rendering works under GLUT. For the moment just notice that
glRasterPos2f(x, y) sets the position where text will be
drawn, also notice that the x, y coordinates used are relative to
the coordinate system of the sub-window (defined in
subReshape()).
The subwindow is acting as a text board for data coming out of
the animation. It is a silly application; we could have just drawn
the text board on the main window and achieved the same result (even
more efficiently). Under certain circumstances however it makes
sense to open a subwindow for text output. For example, when the
animation is in 3D with lights and environmental effects and you do
not wish to deform your text board with annoying lights, perspective
effects, shadows or fog etc. Under these circumstances a subwindow
comes handy because it is completely isolated from the 3D
animation.
There is crucial difference beteween the reshape callback
function for a top level window and for a subwindow. When a reshape
event is triggered, only the top level window reshape callback
function gets invoked, in our example mainReshape(). We
must call the reshape callback function subReshape from
within mainReshape. It clearly makes sense because the
location and shape of the subwindows is constrained to the size and
shape of its top level window. So if you read now the code for
mainReshape() you will see that we first set the projection
matrix for the top level window, then switch to the winIDsub
subwindow and subsequently invoke the subwindow's reshape function
with the width and height relative to winIDMain we wish to
use.
Earlier it was already mentioned that the idle()
callback function must update all the top level and subwindows in
the OpenGL application. In our example, idle() first
updates the animation state variables (time and spin)
and then it requests both main and subwindow to be
redisplayed.
The
keyboard
I have added two active keys to the program. By pushing the
"i"-key you can switch on or off the text board and with the "q"-key
you quit the application. Try them :)
Any time you strike a key in your keyboard, GLUT's event
processing driver registers a keyboard event. These events are
handled by the keyboard callback functions. In principle every
window has its own callback function. When the mouse is in location
(x, y) within a given window (or subwindow) and a keyboard event is
triggered then the keyboard callback function associated with that
window is invoked. This callback function takes as arguments the
ASCII unsigned char associated with the key and the x, y
location of the cursor at that moment. In example2.c there is no use
for x, y but I am sure you can think of applications where you can
take advantage of this nice feature.
Only the top level window in our demo has a keyboard callback. If
you try to press the keys "i" or "q" while the cursor is inside the
subwindow you will notice nothing happens. By default when a window
is created and no keyboard callback is registered then all key
strokes are ignored. Have this in mind in the future if you use
multiple windows and want and active keyboard.
Finally mention that the generation of keyboard callbacks can be
disable by passing NULL to the glutKeyBoardFunc().
Rendering
Text
Rendering text under OpenGL and GLUT sucks!. Sorry to say it but
it is true. I am not completely sure why text rendering has been
neglected from the OpenGL library. The old GL library from SGI had a
few high level functions to handle text rendering in graphics mode
and there was an aditional auxiliary library for changing fonts.
OpenGL only provides very primitive directives for rendering
bitmaps, and that means you have to make your own library of bitmaps
for every character, take care of resolation, scaling of
fonts....you name it!
GLUT solves the dilemma of using text with OpenGL somewhat. It
provides glutBitmapCharacter that renders a single
character onto screen at the location specified by the
glRasterPos. I have added a couple of functions,
drawString(), and drawStringBig() that make your
life a bit easier rendering character string.
Conclusion
This concludes a very simple introduction to the use of GLUT
subwindows. At this point I have to mention that although it is good
to experiment with them and test them under various platforms
unfortunately GLUT subwidows are not fully operational in all
environments. Users with 3Dfx based cards will find that subwindows
are not functional yet due to hardware limitations. Also I have
found a big performance penalty when using subwindows under certain
platforms. For instance, under my Linux Alpha with a 2Mb Matrox
Millenium a subwindow makes the application run about twice as slow,
probably because unfortunately the X server for Alphas still doesn't
support any hardware acceleration. On the other hand, the same
application under windows 95 with a 2Mb ATI RageII with SGI's OpenGL
driver runs wonderfully.
Since Linux development moves so fast it is possible that in the
near future many of this performance problems and incompatibilities
will go away. For the moment simply be aware they exist, so use
multiple windows cautiously.
Of course, advanced users can always find a way around using
subwindows by playing with the matrix stack but since we haven't
studied that yet forgive me if I leave for latter.. ;)).
|